package conta;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Vector;

import excecoes.ContaException;
import excecoes.LocalizacaoException;
import excecoes.MensagemException;
import excecoes.PessoaException;
import excecoes.UsuarioException;
import mensagem.Mensagem;

/**
 * Informa�oes sobre a conta.
 * 
 * @author Adalberto Teixeira
 * @author Andrey Menezes
 * @author Laise Florentino
 * @author Laio Francis
 * 
 */
public class ContaUserStories {
	private char[] caracteresIP = { '0', '1', '2', '3', '4', '5', '6', '7',
			'8', '9', '.' };

	PessoaUserStories pessoa;
	UsuarioUserStories usuario;
	Localizacao localizacao;
	private String ip;

	/**
	 * Cria uma conta.
	 * 
	 * @param userName O userName
	 * @param nome O nome
	 * @param email O email
	 * @param senha A senha
	 * @param telefone O telefone
	 * @param latitude A latitude
	 * @param longitude A longitude
	 * 
	 * @throws UsuarioException
	 * @throws PessoaException
	 */
	public ContaUserStories(String userName, String nome, String email,
			String senha, String telefone, String latitude, String longitude) throws UsuarioException, PessoaException {

		this.pessoa = new PessoaUserStories(nome, email, telefone);
		this.usuario = new UsuarioUserStories(userName, senha);
		this.localizacao = new Localizacao(latitude, longitude);
	}
	
	/**
	 * retorna a pessoa, com suas informações
	 * @return
	 */
	public PessoaUserStories getPessoa(){
		return this.pessoa;
	}
	
	/**
	 * retorna os usuários e suas respectivas informações.
	 * @return
	 */
	public UsuarioUserStories getUsuario(){
		return this.usuario;
	}
	
	/**
	 * retorna a localização do usuário.
	 * @return
	 */
	public Localizacao getLocalizacao(){
		return this.localizacao;
	}
	
	// =-=-=
	//  set
	// =-===
	
	/**
	 * Encaminha a atualizacao de atributos recebidos por conta 
	 * que pertencem a outras classes (recebe apenas um valor).
	 * 
	 * @param atributo Atributo a ser atualizado
	 * @param valor Novo valor do atributo
	 * 
	 * @throws PessoaException
	 * @throws UsuarioException
	 */
	public void atualizar(String atributo, String valor) throws PessoaException, UsuarioException {
		if (atributo.equals("nome"))
			pessoa.setNome(valor);
		if (atributo.equals("email"))
			pessoa.setEmail(valor);
		if (atributo.equals("telefone"))
			pessoa.setTelefone(valor);
		if (atributo.equals("userName"))
			usuario.setUserName(valor);
		if (atributo.equals("senha"))
			usuario.setSenha(valor);
		if (atributo.equals("foto"))
			pessoa.setFoto(valor);
	}
	
	/**
	 * Encaminha a atualizacao de atributos recebidos por conta 
	 * que pertencem a outras classes (recebe dois valores).
	 * 
	 * @param atributo Atributo a ser atualizado
	 * @param valor1 Novo valor 1 
	 * @param valor2 Novo valor 2
	 * 
	 * @throws LocalizacaoException
	 */
	public void atualizar(String atributo, String valor1, String valor2) throws LocalizacaoException{
		if (atributo.equals("localizacao"))
			localizacao.setLocalizacao(valor1, valor2);
	}
	
	/**
	 * Altera o IP.
	 * 
	 * @param ip O IP
	 *            composto unicamente pelos simbolos presentes em { '0', '1',
	 *            '2', '3', '4', '5', '6', '7', '8', '9', '.' }
	 * 
	 * @throws ContaException
	 */
	public void setIp(String ip) throws ContaException {
		// ip vazio
		if (ip == null || ip.equalsIgnoreCase(""))
			throw new ContaException("IP invalido.");
		// fazer a lista
		List<Character> lista = new ArrayList<Character>();
		for (int i = 0; i < caracteresIP.length; i++)
			lista.add(caracteresIP[i]);
		// confere formato ip
		char[] confere = ip.toCharArray();
		for (int i = 0; i < confere.length; i++)
			if (!lista.contains(confere[i]))
				throw new ContaException("IP invalido.");
		// ip tem somente 3 pontos
		int quantidadeDePontos = 0;
		for (int i = 0; i < confere.length; i++)
			if (confere[i] == '.')
				quantidadeDePontos++;
		if (quantidadeDePontos != 3)
			throw new ContaException("IP invalido.");
		// ip tem que ter formato numeros.numeros.numeros.numeros
		// nao existe .0.0.0
		if (ip.charAt(0) == '.') {
			throw new ContaException("IP invalido.");
		}
		// nao exite 0.0.0.
		if (ip.charAt(ip.length() - 1) == '.')
			throw new ContaException("IP invalido.");
		// nao existe 0.0..0
		for (int i = 0; i < ip.length() - 1; i++)
			if (ip.charAt(i) == '.')
				if (ip.charAt(i + 1) == '.')
					throw new ContaException("IP invalido.");

		this.ip = ip;
	}
	
	
	// =-=-=
	//  get
	// =-===
	
	/**
	 * Retorna valor de atributos pertencentes a outras classes.
	 * 
	 * @param atributo Atributo a ter seu valor retornado
	 * @return nome Nome da pessoa que possui a conta
	 * @return email Email da pessoa que possui a conta
	 * @return telefone Telefone da pessoa que possui a conta
	 * @return userName Username do usuario que possui a conta
	 * @return senha Senha do usuario que possui a conta
	 * @return localizacao Localizacao de quem possui a conta
	 */
	public String recolher(String atributo){
		if (atributo.equals("nome"))
			return pessoa.getNome();
		if (atributo.equals("email"))
			return pessoa.getEmail();
		if (atributo.equals("telefone"))
			return pessoa.getTelefone();
		if (atributo.equals("userName"))
			return usuario.getUserName();
		if (atributo.equals("senha"))
			return usuario.getSenha();
		if (atributo.equals("localizacao"))
			return localizacao.getLocalizacao();
		if (atributo.equals("foto"))
			return pessoa.getFoto();
		return "";
	}

	/**
	 * Retorna o IP.
	 * 
	 * @return ip O IP                         
	 */
	public String getIP() {
		return this.ip;
	}
	
	/**
	 * Retorna lista de contatos(amigos).
	 * 
	 * @return String No formato "[Nome Um, Nome Dois, Nome Tres]"                           
	 */
	public String getAmigos() {
		Collections.sort(usuario.contatosUserStories);
		return usuario.contatosUserStories.toString();
	}

	
	// =-=-=-===-=-=-=-=-=-=-
	// metodos add e remove
	// =-===-==-=-==-==-==-=-
	
	/**
	 * Adiciona contato amigo na lista de localizacao compartilhada, 
	 * ou seja, permite que o contato veja a localizacao do usuario da conta.
	 * 
	 * @param userNameAmigo
	 *            eh adicionado quando o amigo esta presente na lista 
	 *            de contatos
	 * 
	 * @throws ContaException
	 *                nulas, vazias, repeticoes ou amigo nao presente
	 *                em lista de contatos           
	 */
	public void addCompartilharLocalizacao(String userNameAmigo)
			throws ContaException {

		if ((userNameAmigo == null || userNameAmigo.equalsIgnoreCase("")))
			throw new ContaException("userNameAmigo incorreto.");
		if (!usuario.contatosUserStories.contains(userNameAmigo))
			throw new ContaException("Usuario desconhecido.");

		if (usuario.amigosCompartilhadosLocalizacao != null)
			if (!usuario.amigosCompartilhadosLocalizacao
					.contains(userNameAmigo))
				this.usuario.amigosCompartilhadosLocalizacao.add(userNameAmigo);
	}

	/**
	 * Adiciona usuario a lista de amigos.
	 * 
	 * @param nomeAmigo
	 *            nao nulo ,vazio ou jah existente em lista de contatos.
	 * 
	 * @throws ContaException
	 *                nulos, vazios ou amigo repetido
	 */
	public void addAmigo(String nomeAmigo) throws ContaException {
		try {
			if (nomeAmigo.equals(null) || nomeAmigo.equalsIgnoreCase(""));
		} catch (NullPointerException e) {
			throw new ContaException("nomeAmigo invalido");
		}
		
		if (!usuario.contatosUserStories.contains(nomeAmigo)) {
			usuario.contatosUserStories.add(nomeAmigo);
		}
	}

	/**
	 * Remove contato da lista de localizacao compartilhada, ou seja, 
	 * nao permite que o contato veja a localizacao do usuario da conta.
	 * 
	 * @param userNameAmigo
	 * 			nao nulo e nao vazio
	 * 
	 * @throws ContaException
	 *           nulo, vazio ou nao existente em lista de contato           
	 */
	public void removeCompartilharLocalizacao(String userNameAmigo)
			throws ContaException {

		if ((userNameAmigo == null || userNameAmigo.equalsIgnoreCase("")))
			throw new ContaException("userNameAmigo incorreto.");

		if (usuario.amigosCompartilhadosLocalizacao != null)
			this.usuario.amigosCompartilhadosLocalizacao
					.remove(userNameAmigo);
	}

	/**
	 * Remove usuario da lista de amigos.
	 * 
	 * @param userNameAmigo
	 *            nao nulo, nao vazio
	 * 
	 * @throws ContaException
	 *            nulos, vazios ou nao existente em lista de contatos
	 */
	public void removeContato(String userNameAmigo) throws ContaException {

		if ((userNameAmigo == null || userNameAmigo.equalsIgnoreCase("")))
			throw new ContaException("userNameAmigo incorreto.");

		if (!usuario.contatosUserStories.contains(userNameAmigo))
			throw new ContaException("Usuario desconhecido.");

		usuario.contatosUserStories.remove(userNameAmigo);
		if(usuario.amigosCompartilhadosLocalizacao.contains(userNameAmigo))
			usuario.amigosCompartilhadosLocalizacao.remove(userNameAmigo);
	}

	
	// =-=-=-===-=-=-=-=-
	// metodos especiais
	// =-===-==-=-==-==-=

	/**
	 * Recebe mensagem enviada para usuario da conta.
	 * 
	 * @param mensagem
	 *            possui campo de tipo que determina onde a mensagem sera
	 *            armazenada
	 * @throws MensagemException
	 *                usuario nao esteja apto a receber determinado tipo de
	 *                mensagem ou caixa de mensagem seja null.
	 */
	public void receberMensagem(Mensagem mensagem) throws MensagemException {
		if (usuario.chatMessageBox == null)
			throw new MensagemException("chatMessageBox null");
		if (usuario.conviteMessageBox == null)
			throw new MensagemException("conviteMessageBox null");
		if (usuario.emailMessageBox == null)
			throw new MensagemException("emailMessageBox null");
		if (usuario.smsMessageBox == null)
			throw new MensagemException("smsMessageBox null");
		if (mensagem.getTipo().equalsIgnoreCase(Mensagem.tipoChat))
			usuario.chatMessageBox.add(mensagem);
		else if (mensagem.getTipo().equalsIgnoreCase(Mensagem.tipoInviteToAdd))
			usuario.conviteMessageBox.add(mensagem);
		else if (mensagem.getTipo().equals(Mensagem.tipoEMail))
			usuario.emailMessageBox.add(mensagem);
		else if (mensagem.getTipo().equalsIgnoreCase(Mensagem.tipoSMS))
			usuario.smsMessageBox.add(mensagem);
		else
			throw new MensagemException("Tipo de mensagem invalida.");
	}

	/**
	 * Retorna conteudo da mensagem recebida por usuario da conta.
	 * 
	 * @param tipo
	 * @param posicaoMensagem
	 * 
	 * @throws ContaException
	 */
	public Mensagem lerMensagem(String tipo, int posicaoMensagem)
			throws ContaException {
		if (usuario.chatMessageBox == null)
			throw new ContaException("chatMessageBox null");
		if (usuario.conviteMessageBox == null)
			throw new ContaException("conviteMessageBox null");
		if (usuario.emailMessageBox == null)
			throw new ContaException("emailMessageBox null");
		if (usuario.smsMessageBox != null)
			throw new ContaException("smsMessageBox null");

		if (tipo.equalsIgnoreCase(Mensagem.tipoChat))
			try {
				return usuario.chatMessageBox.get(posicaoMensagem);
			} catch (Exception e) {
				throw new ContaException(
						"posicaoMensagem invalida informe no intervalo 0 a"
								+ (usuario.chatMessageBox.size() - 1));
			}

		if (tipo.equalsIgnoreCase(Mensagem.tipoInviteToAdd))
			try {
				return usuario.conviteMessageBox.get(posicaoMensagem);
			} catch (Exception e) {
				throw new ContaException(
						"posicaoMensagem invalida informe no intervalo 0 a"
								+ (usuario.conviteMessageBox.size() - 1));
			}

		if (tipo.equalsIgnoreCase(Mensagem.tipoEMail))
			try {
				return usuario.emailMessageBox.get(posicaoMensagem);
			} catch (Exception e) {
				throw new ContaException(
						"posicaoMensagem invalida informe no intervalo 0 a"
								+ (usuario.emailMessageBox.size() - 1));
			}

		if (tipo.equalsIgnoreCase(Mensagem.tipoSMS))
			try {
				return usuario.smsMessageBox.get(posicaoMensagem);
			} catch (Exception e) {
				throw new ContaException(
						"posicaoMensagem invalida informe no intervalo 0 a"
								+ (usuario.smsMessageBox.size() - 1));
			}

		throw new ContaException("tipo de mensagem invalida.");
	}

	/**
	 * Requisita localizacao de contato amigo.
	 * 
	 * @param userNameRequisitante
	 *            nao nulo e nao vazio
	 * @return true - se for permitido false - se nao for permitido
	 * 
	 * @throws ContaException
	 *                nulo, vazio ou lista de amigosCompartilhadosLocalizacao
	 *                ser null.
	 */
	public boolean possoVerLocalizacao(String userNameRequisitante)
			throws ContaException {

		if (userNameRequisitante.equals(null)
				|| userNameRequisitante.equalsIgnoreCase(""))
			throw new ContaException("userNameRequisitante invalido");

		if (usuario.amigosCompartilhadosLocalizacao.equals(null))
			throw new ContaException("amigosCompartilhadosLocalizacao null");
		
		if(!usuario.contatosUserStories.contains(userNameRequisitante))
			throw new ContaException("Usuario desconhecido.");
		
		if (usuario.amigosCompartilhadosLocalizacao
				.contains(userNameRequisitante))
			return true;
		return false;
	}

	/**
	 * Envia convite por email.
	 * 
	 * @param para Destinatario
	 * @return String "Documento convite.txt enviado com sucesso."
	 * 
	 * @throws IOException
	 */
	public String enviarConvite(String para) throws IOException {
		FileWriter fw = new FileWriter("convites.log", true);
		fw
				.write("From: iam@email.com\n"
						+ "to: "
						+ para
						+ "\n"
						+ "Subject: "
						+ pessoa.getNome()
						+ " gostaria de compartilhar sua localiza��o com voc�\n"
						+ "\n"
						+ pessoa.getNome()
						+ " ("
						+ pessoa.getEmail()
						+ ") gostaria de compartilhar sua localiza��o com voc� usando o I'mHere. Voc� tamb�m pode fazer o mesmo com os seus amigos, para mais informa��es acesse http://iamhere.com."
						+ "\nPara aceitar ou recusar, clique no link abaixo. \n"
						+ "\n"
						+ "http://iamhere.com/compartilhar\n"
						+ "\n"
						+ "***************************************************************************************************\n");
		fw.close();
		return "Documento convite.txt enviado com sucesso.";
	}

	public List<String> getListaAmigos() {
		Collections.sort(usuario.contatosUserStories);
		return usuario.contatosUserStories;
	}

}